import os
import time
from threading import Thread

import serial
from pydispatch import dispatcher

from logger import log
from settings import settings


def chk8xor(data):
    xor = 0
    for byte in data:
        xor ^= byte
    return xor


def convert_weight_data_XK3190(buffer, data_buffer):
    for byte in buffer:
        data_buffer['buf2'].append(byte)

    try:
        index = data_buffer['buf2'].index(0x02)
    except ValueError:
        return None

    if index >= 0 and len(data_buffer['buf2']) > 20:
        data_array = data_buffer['buf2'][index + 1:index + 9]
        xor = chk8xor(data_array)
        xor1 = chr(data_buffer['buf2'][index + 9])
        xor2 = chr(data_buffer['buf2'][index + 10])
        data_buffer['buf2'] = []

        if xor1 + xor2 == format(xor, '02X'):
            data_array.pop()
            string = ''.join(chr(byte) for byte in data_array)
            try:
                weight = float(string)
                return weight / settings.multiplier
            except ValueError:
                return None
    return None


class Weight:
    def __init__(self):
        # self.socket = None
        self.serial = None
        self.dataBuffer = {'buf': '', 'buf2': []}
        self.oldWeight = 0
        self.weight = 0
        self.wen = False
        self.wenCount = 0
        self.oldWeightWen = 0
        self.lastTimestamp = time.time()
        self.zeroTimestamp = time.time()
        self.zeroCount = 0

    def checkDataBufferLength(self):
        if len(self.dataBuffer['buf']) > 200:
            self.dataBuffer['buf'] = ''
            log.info('clear DataBuffer')
        if len(self.dataBuffer['buf2']) > 200:
            self.dataBuffer['buf2'] = []
            log.info('clear DataBuffer2')

    def connect_serial(self):
        while True:
            try:
                # 打开串口
                log.info(f"打开串口 {settings.com_path}，波特率 {settings.com_baud_rate}")
                self.serial = serial.Serial(settings.com_path, settings.com_baud_rate, timeout=1)

                while True:
                    # 读取数据
                    if self.serial.in_waiting > 0:
                        buffer = self.serial.read(self.serial.in_waiting)
                        self.loop(buffer)

                    # 休眠一段时间以避免CPU占用过高
                    time.sleep(0.1)
            except Exception as e:
                log.error(f"串口错误: {e}")
                os._exit(1)
            finally:
                if self.serial.is_open:
                    self.serial.close()
                    log.info(f"关闭串口 {settings.com_path}")
            time.sleep(5)

    def check_abnormal(self):
        while True:
            if time.time() - self.lastTimestamp > 60:
                self.weight = 0
                dispatcher.send("weight", weight=0, wen=False)
                log.error("长时间没有接收到数据，程序即将退出")
                os._exit(1)  # 退出程序
            if time.time() - self.zeroTimestamp > 60 * 5:
                # emit 长时间未归0
                pass

            time.sleep(5)

    def check_weight_stability(self):
        while True:
            if self.oldWeightWen == self.weight:
                self.wenCount += 1
                if self.wenCount % settings.weight_wen_timeout == 0 and self.weight != 0:
                    self.wen = True
                    dispatcher.send("weight", weight=self.weight, wen=True)
            else:
                self.wen = False
                dispatcher.send("weight", weight=self.weight, wen=False)
                self.wenCount = 0
                self.oldWeightWen = self.weight
            if self.weight < 0.2:
                self.zeroTimestamp = time.time()
            time.sleep(1)

    def loop(self, buffer):
        self.checkDataBufferLength()
        if settings.equipment_type == "XK3190":
            weight = convert_weight_data_XK3190(buffer, self.dataBuffer)
        else:
            raise f"不支持的型号:{settings.equipment_type}"
        if weight is not None:
            log.info(weight)
            self.weight = weight
            if self.oldWeight != weight:
                self.oldWeight = weight
                self.wen = False
                dispatcher.send("weight", weight=weight, wen=False)
            if self.weight == 0:
                self.zeroCount += 1
                if self.zeroCount > 10:
                    self.zeroCount = 0
                    dispatcher.send("weight", weight=weight, wen=False)

            self.lastTimestamp = time.time()

    def init(self):
        if settings.com_path.find(":") != -1:
            # self.connect_socket()
            pass
        else:
            Thread(target=self.connect_serial, daemon=True).start()

        Thread(target=self.check_weight_stability, daemon=True).start()
        Thread(target=self.check_abnormal, daemon=True).start()
